/* vi:set ts=4 sts=4 sw=4 foldmethod=marker:
 *
 * VIM - Vi IMproved        by Bram Moolenaar
 *              GUI/Motif support by Robert Webb
 *              Macintosh port by Dany St-Amant
 *                        and Axel Kielhorn
 *              Port to MPW by Bernhard Pruemmer
 *              Initial Carbon port by Ammon Skidmore
 *              Initial Cocoa port by Jjgod Jiang
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 * See README.txt for an overview of the Vim source code.
 * 
 * TODO: 
 * 
 * 1. Special key input.
 * 2. Scrollbars.
 * 3. Toolbar.
 * 4. delete_lines and insert_lines.
 * 5. Marked text.
 */

#include "vim.h"
#import <Cocoa/Cocoa.h>

/* Internal Data Structures {{{ */

/* Key mapping {{{2 */

static struct
{
    UniChar function_key;
    int     vim_key;
} function_key_mapping[] =
{
    { NSUpArrowFunctionKey,     K_UP    },
    { NSDownArrowFunctionKey,   K_DOWN  },
    { NSLeftArrowFunctionKey,   K_LEFT  },
    { NSRightArrowFunctionKey,  K_RIGHT },

    { NSF1FunctionKey,          K_F1    },
    { NSF2FunctionKey,          K_F2    },
    { NSF3FunctionKey,          K_F3    },
    { NSF4FunctionKey,          K_F4    },
    { NSF5FunctionKey,          K_F5    },
    { NSF6FunctionKey,          K_F6    },
    { NSF7FunctionKey,          K_F7    },
    { NSF8FunctionKey,          K_F8    },
    { NSF9FunctionKey,          K_F9    },
    { NSF10FunctionKey,         K_F10   },

    { NSF11FunctionKey,         K_F11   },
    { NSF12FunctionKey,         K_F12   },
    { NSF13FunctionKey,         K_F13   },
    { NSF14FunctionKey,         K_F14   },
    { NSF15FunctionKey,         K_F15   },

    { NSInsertFunctionKey,      K_INS   },
    { NSDeleteFunctionKey,      K_DEL   },
    { NSHomeFunctionKey,        K_HOME  },
    { NSEndFunctionKey,         K_END   },

    { NSPageUpFunctionKey,      K_PAGEUP    },
    { NSPageDownFunctionKey,    K_PAGEDOWN  },
    
    { '\t',     '\t'    },  /* tab */
    { '\r',     '\r'    },  /* return */
    { '\003',   '\003'  },  /* enter */
    { '\031',   '\031'  },  /* backtab */
    { '\033',   '\033'  },  /* escape */ 
    { '\177',   K_BS    },  /* backspace */

    /* End of list marker: */
    { 0, 0 }, 
};

/* 2}}} */

#define VIM_MAX_FONT_NAME_LEN   256
#define VIM_DEFAULT_FONT_SIZE   9
#define VIM_DEFAULT_FONT_NAME   (char_u *) "Monaco:h9"

@interface VimTextView: NSView <NSTextInput>
@end

@interface VimAppDelegate: NSObject
- (void) initializeApplicationTimer:(NSTimer *)timer;
- (void) blinkCursorTimer:(NSTimer *)timer;
- (void) menuAction:(id)sender;
@end

struct gui_mac_data {
    NSColor    *fg_color, *bg_color, *sp_color;
    
    NSWindow   *content_window;
    NSView     *content_view;
    NSImage    *content_image;
    NSRect      content_rect;
    
    NSFont     *current_font;
    
    int         app_is_running;
    float       bg_alpha;
    
    int         blink_state;
    long        blink_wait;
    long        blink_on;
    long        blink_off;
    NSTimer    *blink_timer;
    
    NSDate     *last_draw_string;
    bool        disable_redraw;
        
    VimAppDelegate    *app_delegate;
    NSAutoreleasePool *app_pool;
} gui_mac;

#define FLIPPED_RECT(rect)  NSMakeRect(rect.origin.x, \
                                       gui_mac.content_rect.size.height - rect.origin.y - rect.size.height, \
                                       rect.size.width, \
                                       rect.size.height)

#define run_app()           gui_mac.app_is_running = TRUE
#define stop_app()          gui_mac.app_is_running = FALSE
#define app_is_running()    (gui_mac.app_is_running == TRUE)

/* Data Structures }}}*/

/* Internal functions prototypes {{{ */

@interface NSApplication (VimAdditions)
-(void) setAppleMenu:(NSMenu *)aMenu;
@end

int hex_digit(int c);
NSColor *NSColorFromGuiColor(guicolor_T color, float alpha);

int create_window(NSRect rect);
void set_application_menu();

void redraw();

unsigned int modifiers_to_vim(unsigned int mac_modifiers);
int function_key_to_vim(UniChar key_char, unsigned int vim_modifiers);

GuiFont find_font(char_u *font_name);
int points_to_pixels(char_u *str, char_u **end);
NSFont *get_font(char_u *font_name, int size);

#define NSShowRect(msg, rect)        NSLog(@"%s: %g %g %g %g", msg, \
                                           rect.origin.x, rect.origin.y, \
                                           rect.size.width, rect.size.height)

/* Internal functions prototypes }}} */

/* Initializtion and Finalization {{{ */

int gui_mch_init()
{
    fprintf(stderr, "gui_mch_init\n");

    gui.char_width = 7;
    gui.char_height = 11;
    gui.char_ascent = 6;
    gui.num_rows = 24;
    gui.num_cols = 80;
    gui.in_focus = TRUE;

    gui.norm_pixel = 0x00000000;
    gui.back_pixel = 0x00FFFFFF;
    set_normal_colors();

    gui_check_colors();
    gui.def_norm_pixel = gui.norm_pixel;
    gui.def_back_pixel = gui.back_pixel;

    /* Get the colors for the highlight groups (gui_check_colors() might have
     * changed them) */
    highlight_gui_started();

#ifdef FEAT_MENU
    gui.menu_height = 0;
#endif
    gui.scrollbar_height = gui.scrollbar_width = 0;
    gui.border_offset = gui.border_width = 0;
    
    gui_mac.bg_alpha = 1.0;
    
    gui_mac.content_window = nil;
    gui_mac.content_view  = nil;
    gui_mac.content_image = nil;
    gui_mac.last_draw_string = nil;
    gui_mac.disable_redraw = NO;

    return OK;
}

int gui_mch_init_check()
{
    fprintf(stderr, "gui_mch_init_check\n");

    /* see main.c for reason to disallow */
    if (disallow_gui) 
        return FAIL;

    return OK;
}

void gui_mch_exit(int rc)
{
    fprintf(stderr, "gui_mch_exit\n");

    exit(rc);
}

int gui_mch_open()
{
    fprintf(stderr, "gui_mch_open\n");

    if (gui_win_x != -1 && gui_win_y != -1)
        gui_mch_set_winpos(gui_win_x, gui_win_y);

    return OK;
}

void gui_mch_prepare(int *argc, char **argv)
{
    fprintf(stderr, "gui_mch_prepare\n");

    gui_mac.app_pool = [NSAutoreleasePool new];

    [NSApplication sharedApplication];
    
    gui_mac.app_delegate = [VimAppDelegate new];
    [NSApp setDelegate: gui_mac.app_delegate];

    [NSApp setMainMenu: [[NSMenu new] autorelease]];
    set_application_menu();
}

void gui_mch_set_shellsize(
    int width,
    int height,
    int min_width,
    int min_height,
    int base_width,
    int base_height,
    int direction)
{    
    NSLog(@"gui_mch_set_shellsize: "
          "(%d, %d, %d, %d, %d, %d, %d)\n", 
            width, height, min_width, min_height, 
            base_width, base_height, direction);
            
    fprintf(stderr, "gui.num_rows (%d) * gui.char_height (%d) = %d\n", 
            gui.num_rows, gui.char_height, gui.num_rows * gui.char_height);

    fprintf(stderr, "gui.num_cols (%d) * gui.char_width (%d) = %d\n", 
            gui.num_cols, gui.char_width, gui.num_cols * gui.char_width);
            
    if (gui_mac.content_image == nil)
    {
        gui_mac.content_image = [[NSImage alloc] initWithSize: NSMakeSize(width, height)];
        [gui_mac.content_image setFlipped: YES];
    }

    gui_mac.content_rect = NSMakeRect(0, 0, width, height);
    // [gui_mac.content_window setContentSize: NSMakeSize(width, height)];
}

void gui_mch_set_text_area_pos(int x, int y, int w, int h)
{
    fprintf(stderr, "gui_mch_set_text_area_pos: "
            "%d, %d, %d, %d\n", x, y, w, h);
}

/* Initializtion and Finalization }}} */

/* Event related {{{ */

void gui_mch_update()
{
    gui_mch_wait_for_chars(0);
}

/* wtime < 0: wait forever
 * wtime > 0: wait wtime milliseconds
 * wtime = 0: don't wait, only poll existing events
 */
int gui_mch_wait_for_chars(int wtime)
{
    NSEvent *event;
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSDate  *date;
    static   int first_time = 1;

    if (wtime == 0)
    {
        // NSLog(@"gui_mch_wait_for_chars: don't wait");
        date = [NSDate distantPast];
    } 
    else if (wtime > 0)
    {
        // NSLog(@"gui_mch_wait_for_chars: wait for %d ms", wtime);
        date = [NSDate dateWithTimeIntervalSinceNow: (double) wtime / 1000.0];
    }
    /* wtime < 0, wait forever */
    else
    {
        // NSLog(@"gui_mch_wait_for_chars: wait forever");
        date = [NSDate distantFuture];
    }
    
    /* It's tricky here: we don't want to use -[NSApplication run:] 
     * all the time, but we need it to do some initialization for
     * the first time this app starts. So we install a timer to 
     * stop: NSApp just after it runs.
     */
    if (first_time)
    {
        NSLog(@"first time, begin initialization...");
        first_time = 0;
        [NSTimer scheduledTimerWithTimeInterval: 0.1
                                         target: gui_mac.app_delegate
                                       selector: @selector(initializeApplicationTimer:)
                                       userInfo: 0
                                        repeats: NO];
        [NSApp run];
        NSLog(@"end initialization.");
    }

    run_app();
    while (app_is_running() && 
           (event = [NSApp nextEventMatchingMask: NSAnyEventMask
                                       untilDate: date
                                          inMode: NSDefaultRunLoopMode
                                         dequeue: YES]) != nil)
    {
        [NSApp sendEvent: event];
    }

    [pool release];
    return OK;
}

/* Event related }}} */

/* Input Method Handling {{{ */

int im_get_status()
{
    if (! gui.in_use)
            return 0;

    return 0;
}

void im_set_active(int active)
{
}

void im_set_position(int row, int col)
{
}

/* Input Method Handling }}} */

/* Misc Stuff {{{ */

void gui_mch_get_screen_dimensions(int *screen_w, int *screen_h)
{
    CGRect rect;

    rect = CGDisplayBounds(CGMainDisplayID());

    *screen_w = (int) rect.size.width;
    *screen_h = (int) rect.size.height;

    fprintf(stderr, "gui_mch_get_screen_dimensions: %d, %d\n", 
            *screen_w, *screen_h);
}

#ifdef USE_MCH_ERRMSG

void display_errors()
{
    fprintf(stderr, "display_errors\n");
    fflush(stderr);
}

#endif

int gui_mch_haskey(char_u *name)
{
    return OK;
}

void gui_mch_beep()
{
    NSBeep();
}

void gui_mch_toggle_tearoffs(int enable)
{
    /* no tearoff menus */
}

/* Misc Stuff }}} */

/* Font Handling {{{ */

int gui_mch_init_font(char_u *font_name, int fontset)
{
    NSAutoreleasePool *pool;
    NSFont  *mac_font;
    GuiFont  vim_font;
    NSSize   advance;
    char_u   used_font_name[VIM_MAX_FONT_NAME_LEN];

    if (font_name == NULL)
        font_name = VIM_DEFAULT_FONT_NAME;

    fprintf(stderr, "gui_mch_init_font: %s\n", font_name);

    if (STRCMP(font_name, "*") == 0)
        return FAIL;

    pool = [NSAutoreleasePool new];
    vim_font = find_font(font_name);

    if (vim_font == NOFONT)
    {
        NSLog(@"find_font failed\n");
        return FAIL;
    }

    gui.norm_font = vim_font;

    vim_strncpy(used_font_name, font_name, sizeof(used_font_name) - 1);
    hl_set_font_name(used_font_name);

    mac_font = (NSFont *) vim_font;
    advance  = [mac_font maximumAdvancement];

    /* in 72 DPI, 1 point = 1 pixel */
    gui.char_ascent = roundf([mac_font ascender]);
    gui.char_width  = roundf(advance.width);
    gui.char_height = roundf([mac_font ascender] - [mac_font descender]) + p_linespace;
    
    [gui_mac.content_window setResizeIncrements: NSMakeSize(gui.char_width, gui.char_height)];
    
    fprintf(stderr, "ascent = %d, width = %d, height = %d, %f, %f\n", 
            gui.char_ascent, gui.char_width, gui.char_height, 
            [mac_font ascender], [mac_font descender]);

    [pool release];
    return OK;
}

void gui_mch_free_font(GuiFont font)
{
    [(NSFont *) font release];
}

void gui_mch_set_font(GuiFont font)
{
    // fprintf(stderr, "gui_mch_set_font\n");
    gui_mac.current_font = (NSFont *) font;
}

GuiFont gui_mch_get_font(char_u *name, int giveErrorIfMissing)
{
    GuiFont font;

    fprintf(stderr, "gui_mch_get_font: %s\n", name);
    font = find_font(name);

    if (font == NOFONT)
    {
	    if (giveErrorIfMissing)
    	    EMSG2(_(e_font), name);
    	return NOFONT;
    }
    /*
     * TODO : Accept only monospace
     */

    return font;
}

char_u *gui_mch_get_fontname(GuiFont font, char_u *name)
{
    if (name == NULL)
        return NULL;

    return vim_strsave(name);
}

int gui_mch_adjust_charheight()
{
    NSFont *mac_font = gui_mac.current_font;
    
    if (mac_font == nil)
        return OK;

    /* in 72 DPI, 1 point = 1 pixel */
    gui.char_ascent = roundf([mac_font ascender]);
    gui.char_height = roundf([mac_font ascender] - [mac_font descender]) + p_linespace;
    
    return OK;
}

/* Font Handling }}} */

/* Window Handling {{{ */

void gui_mch_set_foreground()
{
    fprintf(stderr, "gui_mch_set_foreground\n");
    
    [gui_mac.content_window orderFront: nil];
}

void gui_mch_set_winpos(int x, int y)
{
    fprintf(stderr, "gui_mch_set_winpos: %d, %d\n", x, y);
    
    [gui_mac.content_window setFrameTopLeftPoint: NSMakePoint(x, y)];
}

int gui_mch_get_winpos(int *x, int *y)
{
    NSRect rect = [gui_mac.content_window frame];

    *x = (int) rect.origin.x;
    *y = (int) rect.origin.y;

    return OK;
}

void gui_mch_settitle(char_u *title, char_u *icon)
{
    fprintf(stderr, "gui_mch_set_title: (%s, %s)\n", title, icon);

    [gui_mac.content_window setTitle: [NSString stringWithUTF8String: (const char *) title]];
}

void gui_mch_iconify()
{
    fprintf(stderr, "gui_mch_iconify\n");
}

/* Window Handling }}} */

/* Menu Handling {{{ */

/* TODO: 
 *
 * 1. popup menus are ignored. 
 * 2. command ID has not been assigned.
 */
 
NSMenuItem *insert_menu_item(vimmenu_T *menu)
{
    vimmenu_T  *parent, *brother;
    NSMenu     *parent_menu;
    NSMenuItem *mac_menu_item, *item;
    int         alloc = 0, index;

    brother = menu->next;
    /* My brother could be the PopUp, find my real brother */
    while ((brother != NULL) && (! menu_is_menubar(brother->name)))
	    brother = brother->next;

    if (STRNCMP(menu->dname, "-SEP", 4) == 0 || 
        STRNCMP(menu->dname, "-sep", 4) == 0)
        mac_menu_item = [NSMenuItem separatorItem];
    else
    {
        NSString *title = [NSString stringWithUTF8String: (const char *) menu->dname];
        mac_menu_item = [[NSMenuItem alloc] initWithTitle: title 
                                                   action: @selector(menuAction:)
                                            keyEquivalent: @""];
        [mac_menu_item setTarget: gui_mac.app_delegate];
        [mac_menu_item setTag: (int) menu];
        alloc = 1;
        
        if (menu->actext != NULL)
        {
            NSString *tooltip = [NSString stringWithUTF8String: (const char *) menu->actext];
            
            [mac_menu_item setToolTip: tooltip];
        }
    }
    menu->item_handle = (void *) mac_menu_item;
                                      
    parent = menu->parent;
    if (parent == NULL)
        parent_menu = [NSApp mainMenu];
    else
        parent_menu = (NSMenu *) parent->menu_handle;

    /* If index == -1, means in parent menu we cannot find 
     * this menu item, must be something wrong, but we still
     * need to handle this gracefully */
    if (brother != NULL && 
        (item = (NSMenuItem *) brother->item_handle) != NULL &&
        (index = [parent_menu indexOfItem: item]) != -1)
        [parent_menu insertItem: mac_menu_item
                        atIndex: index];
    else
        [parent_menu addItem: mac_menu_item];
    
    if (alloc)
        [mac_menu_item release];
    return mac_menu_item;
}

void gui_mch_add_menu(vimmenu_T *menu, int idx)
{
    // fprintf(stderr, "gui_mch_add_menu: %s, %d\n", menu->dname, idx);
    
    if (! menu_is_menubar(menu->name))
        return;

    NSString *title = [NSString stringWithUTF8String: (const char *) menu->dname];
    NSMenu *mac_menu = [[NSMenu alloc] initWithTitle: title];
    NSMenuItem *mac_menu_item = insert_menu_item(menu);

    [mac_menu_item setSubmenu: mac_menu];
    menu->menu_handle = (void *) mac_menu;

    [mac_menu release];
}

void gui_mch_add_menu_item(vimmenu_T *menu, int idx)
{
    // fprintf(stderr, "gui_mch_add_menu_item: %s, %d\n", menu->dname, idx);
    
    insert_menu_item(menu);
}

void gui_mch_destroy_menu(vimmenu_T *menu)
{
    NSMenu *parent_menu;

    if (menu == NULL)
        return;
        
    if (menu->parent == NULL)
        parent_menu = [NSApp mainMenu];
    else
        parent_menu = (NSMenu *) menu->parent->menu_handle;
        
    [parent_menu removeItem: (NSMenuItem *) menu->item_handle];
    menu->item_handle = NULL;
}

void gui_mch_draw_menubar()
{
}

void gui_mch_menu_grey(vimmenu_T *menu, int grey)
{
    NSMenuItem *item;

    if (menu == NULL)
        return;
    
    item = (NSMenuItem *) menu->item_handle;
    [item setEnabled: grey ? NO : YES];
}

void gui_mch_menu_hidden(vimmenu_T *menu, int hidden)
{
    /* There's no hidden mode on MacOS */
    gui_mch_menu_grey(menu, hidden);
}

void gui_mch_show_popupmenu(vimmenu_T *menu)
{
}

void gui_mch_enable_menu(int flag)
{
    /* menu is always active */
}

void gui_mch_set_menu_pos(int x, int y, int w, int h)
{
    /* menu position is fixed, always at the top */
}

/* Menu Handling }}} */

/* Dialog related {{{ */

char_u *gui_mch_browse(
    int saving,
    char_u *title,
    char_u *dflt,
    char_u *ext,
    char_u *initdir,
    char_u *filter)
{
    return NULL;
}

int gui_mch_dialog(
    int     type,
    char_u  *title,
    char_u  *message,
    char_u  *buttons,
    int     dfltbutton,
    char_u  *textfield)
{
    return 1;
}

/* Dialog related }}} */

/* Color related {{{ */

/* Colors Macros */
#define RGB(r,g,b)  ((r) << 16) + ((g) << 8) + (b)
#define Red(c)      ((c & 0x00FF0000) >> 16)
#define Green(c)    ((c & 0x0000FF00) >>  8)
#define Blue(c)     ((c & 0x000000FF) >>  0)

long_u gui_mch_get_rgb(guicolor_T pixel)
{
    return (Red(pixel) << 16) + (Green(pixel) << 8) + Blue(pixel);
}

void gui_mch_new_colors()
{
}

guicolor_T gui_mch_get_color(char_u *name)
{
    typedef struct guicolor_tTable
    {
        char        *name;
        guicolor_T  color;
    } guicolor_tTable;

    /*
     * The comment at the end of each line is the source
     * (Mac, Window, Unix) and the number is the unix rgb.txt value
     */
    static guicolor_tTable table[] =
    {
    { "Black",      RGB(0x00, 0x00, 0x00) },
    { "darkgray",   RGB(0x80, 0x80, 0x80) }, /*W*/
    { "darkgrey",   RGB(0x80, 0x80, 0x80) }, /*W*/
    { "Gray",       RGB(0xC0, 0xC0, 0xC0) }, /*W*/
    { "Grey",       RGB(0xC0, 0xC0, 0xC0) }, /*W*/
    { "lightgray",  RGB(0xE0, 0xE0, 0xE0) }, /*W*/
    { "lightgrey",  RGB(0xE0, 0xE0, 0xE0) }, /*W*/
    { "gray10",     RGB(0x1A, 0x1A, 0x1A) }, /*W*/
    { "grey10",     RGB(0x1A, 0x1A, 0x1A) }, /*W*/
    { "gray20",     RGB(0x33, 0x33, 0x33) }, /*W*/
    { "grey20",     RGB(0x33, 0x33, 0x33) }, /*W*/
    { "gray30",     RGB(0x4D, 0x4D, 0x4D) }, /*W*/
    { "grey30",     RGB(0x4D, 0x4D, 0x4D) }, /*W*/
    { "gray40",     RGB(0x66, 0x66, 0x66) }, /*W*/
    { "grey40",     RGB(0x66, 0x66, 0x66) }, /*W*/
    { "gray50",     RGB(0x7F, 0x7F, 0x7F) }, /*W*/
    { "grey50",     RGB(0x7F, 0x7F, 0x7F) }, /*W*/
    { "gray60",     RGB(0x99, 0x99, 0x99) }, /*W*/
    { "grey60",     RGB(0x99, 0x99, 0x99) }, /*W*/
    { "gray70",     RGB(0xB3, 0xB3, 0xB3) }, /*W*/
    { "grey70",     RGB(0xB3, 0xB3, 0xB3) }, /*W*/
    { "gray80",     RGB(0xCC, 0xCC, 0xCC) }, /*W*/
    { "grey80",     RGB(0xCC, 0xCC, 0xCC) }, /*W*/
    { "gray90",     RGB(0xE5, 0xE5, 0xE5) }, /*W*/
    { "grey90",     RGB(0xE5, 0xE5, 0xE5) }, /*W*/
    { "white",      RGB(0xFF, 0xFF, 0xFF) },
    { "darkred",    RGB(0x80, 0x00, 0x00) }, /*W*/
    { "red",        RGB(0xDD, 0x08, 0x06) }, /*M*/
    { "lightred",   RGB(0xFF, 0xA0, 0xA0) }, /*W*/
    { "DarkBlue",   RGB(0x00, 0x00, 0x80) }, /*W*/
    { "Blue",       RGB(0x00, 0x00, 0xD4) }, /*M*/
    { "lightblue",  RGB(0xA0, 0xA0, 0xFF) }, /*W*/
    { "DarkGreen",  RGB(0x00, 0x80, 0x00) }, /*W*/
    { "Green",      RGB(0x00, 0x64, 0x11) }, /*M*/
    { "lightgreen", RGB(0xA0, 0xFF, 0xA0) }, /*W*/
    { "DarkCyan",   RGB(0x00, 0x80, 0x80) }, /*W ?0x307D7E */
    { "cyan",       RGB(0x02, 0xAB, 0xEA) }, /*M*/
    { "lightcyan",  RGB(0xA0, 0xFF, 0xFF) }, /*W*/
    { "darkmagenta",RGB(0x80, 0x00, 0x80) }, /*W*/
    { "magenta",    RGB(0xF2, 0x08, 0x84) }, /*M*/
    { "lightmagenta",RGB(0xF0, 0xA0, 0xF0) }, /*W*/
    { "brown",      RGB(0x80, 0x40, 0x40) }, /*W*/
    { "yellow",     RGB(0xFC, 0xF3, 0x05) }, /*M*/
    { "lightyellow",RGB(0xFF, 0xFF, 0xA0) }, /*M*/
    { "darkyellow", RGB(0xBB, 0xBB, 0x00) }, /*U*/
    { "SeaGreen",   RGB(0x2E, 0x8B, 0x57) }, /*W 0x4E8975 */
    { "orange",     RGB(0xFC, 0x80, 0x00) }, /*W 0xF87A17 */
    { "Purple",     RGB(0xA0, 0x20, 0xF0) }, /*W 0x8e35e5 */
    { "SlateBlue",  RGB(0x6A, 0x5A, 0xCD) }, /*W 0x737CA1 */
    { "Violet",     RGB(0x8D, 0x38, 0xC9) }, /*U*/
    };

    int r, g, b;
    int i;

    if (name[0] == '#' && strlen((char *) name) == 7)
    {
        /* Name is in "#rrggbb" format */
        r = hex_digit(name[1]) * 16 + hex_digit(name[2]);
        g = hex_digit(name[3]) * 16 + hex_digit(name[4]);
        b = hex_digit(name[5]) * 16 + hex_digit(name[6]);
        if (r < 0 || g < 0 || b < 0)
            return INVALCOLOR;
        return RGB(r, g, b);
    }
    else
    {
        /* Check if the name is one of the colors we know */
        for (i = 0; i < sizeof(table) / sizeof(table[0]); i++)
            if (STRICMP(name, table[i].name) == 0)
            return table[i].color;
    }

    return INVALCOLOR;
}

void gui_mch_set_fg_color(guicolor_T color)
{
    gui_mac.fg_color = NSColorFromGuiColor(color, 1.0);
}

void gui_mch_set_bg_color(guicolor_T color)
{
    gui_mac.bg_color = NSColorFromGuiColor(color, gui_mac.bg_alpha);
}

void gui_mch_set_sp_color(guicolor_T color)
{
    gui_mac.sp_color = NSColorFromGuiColor(color, 1.0);
}

/* Color related }}} */

/* Drawing related {{{ */

void gui_mch_flush()
{
    // fprintf(stderr, "gui_mch_flush\n");
    // redraw();
}

static inline CGRect CGRectFromNSRect(NSRect nsRect) { return *(CGRect*)&nsRect; }

void gui_mch_invert_rectangle(int r, int c, int nr, int nc)
{
    NSRect rect;
    fprintf(stderr, "gui_mch_invert_rectangle\n");
    
    [gui_mac.content_image lockFocus];

    rect = NSMakeRect(FILL_X(c), 
                      FILL_Y(r), 
                      FILL_X(nc), 
                      FILL_Y(nr));

    CGContextRef context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
    CGContextSaveGState (context);
    CGContextSetBlendMode(context, kCGBlendModeDifference);
    CGContextSetRGBFillColor (context, 1.0, 1.0, 1.0, 1.0);
    CGContextFillRect (context, CGRectFromNSRect(rect));
    CGContextRestoreGState (context);

    [gui_mac.content_image unlockFocus];
    [gui_mac.content_view setNeedsDisplayInRect: FLIPPED_RECT(rect)]; 
}

void gui_mch_flash(int msec)
{
    fprintf(stderr, "gui_mch_flash\n");
    
    redraw();
}

void gui_mch_clear_all()
{
    fprintf(stderr, "gui_mch_clear_all\n");

    gui_mch_set_bg_color(gui.back_pixel);
    
    [gui_mac.content_image lockFocus];

    [gui_mac.bg_color set];
    
    // NSShowRect("clear_all: content_rect", gui_mac.content_rect);
    NSRectFill(gui_mac.content_rect);

    [gui_mac.content_image unlockFocus];
    
    if (gui_mac.disable_redraw == YES)
        gui_mac.disable_redraw = NO;
    else
        redraw();
}

void gui_mch_clear_block(int row1, int col1, int row2, int col2)
{
    NSRect rect;
    int x, y, w, h;

    x = FILL_X(col1);
    y = FILL_Y(row1);
    w = FILL_X(col2 + 1) + (col2 == Columns - 1) - x;
    h = FILL_Y(row2 + 1) - y;
    
    fprintf(stderr, "gui_mch_clear_block = (%d, %d) - (%d, %d) "
            "-- x = %d, y = %d, w = %d, h = %d\n", 
            row1, col1, row2, col2, x, y, w, h);

    gui_mch_set_bg_color(gui.back_pixel);

    [gui_mac.content_image lockFocus];

    [gui_mac.bg_color set];
    rect = NSMakeRect(x, y, w, h);

    NSRectFill(rect);

    [gui_mac.content_image unlockFocus];    
    
    // if (gui_mac.disable_redraw == NO)
    //    [gui_mac.content_view setNeedsDisplayInRect: FLIPPED_RECT(rect)];
    // else
    //    gui_mac.disable_redraw = NO;
}

void gui_mch_delete_lines(int row, int num_lines)
{
    NSRect deleted_rect, moved_rect;
    int width, height;

    fprintf(stderr, "gui_mch_delete_lines: row = %d, num = %d\n", 
            row, num_lines);
            
    width = FILL_X(gui.scroll_region_right + 1 - 
                   gui.scroll_region_left);
    height = FILL_Y(gui.scroll_region_bot + 1 - row - num_lines);

    deleted_rect = NSMakeRect(gui.scroll_region_left, 
                              FILL_Y(row), 
                              width, height);
                              
    moved_rect = NSMakeRect(gui.scroll_region_left, 
                            FILL_Y(row + num_lines), 
                            width, height);

    NSImage *temp_image = [[NSImage alloc] initWithSize: moved_rect.size];
    [temp_image setFlipped: YES];

    [temp_image lockFocus];
    [gui_mac.content_image drawInRect: NSMakeRect(0, 0, width, height)
                             fromRect: moved_rect
                            operation: NSCompositeCopy
                             fraction: 1.0];
    [temp_image unlockFocus];
    
    [gui_mac.content_image lockFocus];
    
    [temp_image drawInRect: deleted_rect
                  fromRect: NSZeroRect
                 operation: NSCompositeCopy
                  fraction: 1.0];
        
    [gui_mac.content_image unlockFocus];
    
    [temp_image release];
    
    gui_clear_block(gui.scroll_region_bot - num_lines + 1,
    				gui.scroll_region_left,
    	            gui.scroll_region_bot, 
    	            gui.scroll_region_right);
    [gui_mac.content_view setNeedsDisplayInRect: FLIPPED_RECT(deleted_rect)]; 
}

void gui_mch_insert_lines(int row, int num_lines)
{
    NSRect moved_rect, to_rect;
    int width, height;

    fprintf(stderr, "gui_mch_insert_lines: row = %d, num = %d\n", 
            row, num_lines);
            
    [gui_mac.content_view displayIfNeeded];
            
    width = FILL_X(gui.scroll_region_right + 1 - gui.scroll_region_left);
    height = FILL_Y(gui.scroll_region_bot + 1 - row - num_lines);

    moved_rect = NSMakeRect(FILL_X(gui.scroll_region_left), 
                            FILL_Y(row), 
                            width, height);

    to_rect = NSMakeRect(FILL_X(gui.scroll_region_left), 
                         FILL_Y(row + num_lines), 
                         width, height);
                            
    NSImage *temp_image = [[NSImage alloc] initWithSize: moved_rect.size];
    [temp_image setFlipped: YES];

    [temp_image lockFocus];
    [gui_mac.content_image drawInRect: NSMakeRect(0, 0, width, height)
                             fromRect: moved_rect
                            operation: NSCompositeCopy
                             fraction: 1.0];
    [temp_image unlockFocus];
    
    // [[temp_image TIFFRepresentation] writeToFile: @"/Users/jjgod/temp-image.tiff" 
    //                                  atomically: NO];
    
    [gui_mac.content_image lockFocus];
    
    [temp_image drawInRect: to_rect
                  fromRect: moved_rect
                 operation: NSCompositeCopy
                  fraction: 1.0];

    [gui_mac.content_image unlockFocus];
    [temp_image release];

    /* Update gui.cursor_row if the cursor scrolled or copied over */
    if (gui.cursor_row >= gui.row
	    && gui.cursor_col >= gui.scroll_region_left
	    && gui.cursor_col <= gui.scroll_region_right)
    {
	    if (gui.cursor_row <= gui.scroll_region_bot - num_lines)
    	    gui.cursor_row += num_lines;
    	else if (gui.cursor_row <= gui.scroll_region_bot)
    	    gui.cursor_is_valid = FALSE;
    }
    
    gui_mac.disable_redraw = YES;

    // [gui_mac.content_view setNeedsDisplayInRect: FLIPPED_RECT(moved_rect)];
    gui_clear_block(row, gui.scroll_region_left,
				    row + num_lines - 1, 
				    gui.scroll_region_right);
}

void gui_mch_draw_hollow_cursor(guicolor_T color)
{
    fprintf(stderr, "gui_mch_draw_hollow_cursor\n");
}

void gui_mch_draw_part_cursor(int w, int h, guicolor_T color)
{
    NSRect rect;
    int    left;
    
    fprintf(stderr, "gui_mch_draw_part_cursor: %d, %d (%d, %d)\n", w, h, gui.col, gui.row);
    
#ifdef FEAT_RIGHTLEFT
    /* vertical line should be on the right of current point */
    if (CURSOR_BAR_RIGHT)
        left = FILL_X(gui.col + 1) - w;
    else
#endif
    left = FILL_X(gui.col);

    rect = NSMakeRect(left, 
                      FILL_Y(gui.row) + gui.char_height - h, 
                      1, h);

    [gui_mac.content_image lockFocus];
    
    [NSColorFromGuiColor(color, 1.0) set];
    NSLog(@"rect = %g %g %g %g", 
          rect.origin.x, rect.origin.y, 
          rect.size.width, rect.size.height);
    [NSBezierPath fillRect: rect];

    [gui_mac.content_image unlockFocus];    
    [gui_mac.content_view setNeedsDisplayInRect: FLIPPED_RECT(rect)];
}

void gui_mch_draw_string(int row, int col, char_u *s, int len, int flags)
{
    NSAutoreleasePool *pool = [NSAutoreleasePool new];
    NSPoint       start_point;
    NSString     *string_to_draw;
    NSMutableDictionary *string_attributes;
    char_u        ch;
    NSRect        rect;
    NSSize        desc_size;

    NSLog(@"gui_mch_draw_string: (%d, %d, %d)", row, col, len);
    
    [gui_mac.content_image lockFocus];         
    start_point = NSMakePoint(FILL_X(col), FILL_Y(row));
    
    string_attributes = [NSMutableDictionary dictionaryWithObjectsAndKeys: 
                            gui_mac.fg_color, 
                            NSForegroundColorAttributeName,
                            gui_mac.current_font, NSFontAttributeName, nil];
                            
    if (! (flags & DRAW_TRANSP))
    {
        [string_attributes setObject: gui_mac.bg_color 
                              forKey: NSBackgroundColorAttributeName];
    }
    
    if (flags & DRAW_UNDERL)
    {
        [string_attributes setObject: [NSNumber numberWithInt: NSUnderlineStyleSingle]
                              forKey: NSUnderlineStyleAttributeName];
    }
    
    ch = s[len];
    s[len] = '\0';
    string_to_draw = [NSString stringWithUTF8String: (const char *) s];
    s[len] = ch;
    
    desc_size = [string_to_draw sizeWithAttributes: string_attributes];
    [string_to_draw drawAtPoint: start_point 
                 withAttributes: string_attributes];

    [gui_mac.content_image unlockFocus];
    [pool release];
    
    rect = NSMakeRect(FILL_X(col), 
                      FILL_Y(row), 
                      desc_size.width, 
                      gui.char_height);

    gui_mac.last_draw_string = [NSDate date];
    // if (gui_mac.disable_redraw == NO)
        [gui_mac.content_view setNeedsDisplayInRect: FLIPPED_RECT(rect)];
}

/* Drawing related }}} */

/* Clipboard related {{{ */

void clip_mch_lose_selection(VimClipboard *cbd)
{
}

int clip_mch_own_selection(VimClipboard *cbd)
{
    return OK;
}

void clip_mch_set_selection(VimClipboard *cbd)
{
}

void clip_mch_request_selection(VimClipboard *cbd)
{
}

/* Clipboard related }}} */

/* Scrollbar related {{{ */

void gui_mch_create_scrollbar(scrollbar_T *sb, int orient)
{
    fprintf(stderr, "gui_mch_create_scrollbar: ident = %ld, "
                    "type = %d, value = %ld, size = %ld, "
                    "max = %ld, top = %d, height = %d, "
                    "width = %d, status_height = %d\n", 
            sb->ident, sb->type, sb->value, sb->size, sb->max, 
            sb->top, sb->height, sb->width, sb->status_height);
            
    NSScroller *scroller;
    
    scroller = [[NSScroller alloc] init];
    sb->scroller = (void *) scroller;
}

void gui_mch_destroy_scrollbar(scrollbar_T *sb)
{
    NSScroller *scroller = (NSScroller *) sb->scroller;
    
    if (scroller != nil)
        [scroller release];
        
    sb->scroller = NULL;
}

void gui_mch_enable_scrollbar(scrollbar_T *sb, int flag)
{
    fprintf(stderr, "enable scrollbar: %ld - %d\n", 
            sb->ident, flag);
}

void gui_mch_set_scrollbar_pos(
    scrollbar_T *sb,
    int x,
    int y,
    int w,
    int h)
{
    fprintf(stderr, "set scrollbar pos: %ld, (%d, %d, %d, %d)\n", 
            sb->ident, x, y, w, h);
}

void gui_mch_set_scrollbar_thumb(
    scrollbar_T *sb,
    long val,
    long size,
    long max)
{
}

/* Scrollbar related }}} */

/* Mouse related {{{ */

void gui_mch_getmouse(int *x, int *y)
{
}

void gui_mch_setmouse(int x, int y)
{
}

/* Mouse related }}} */

/* Cursor blinking stuff {{{ */

enum blink_state {
    BLINK_NONE,     /* not blinking at all */
    BLINK_OFF,      /* blinking, cursor is not shown */
    BLINK_ON        /* blinking, cursor is shown */
};

void gui_mch_set_blinking(long wait, long on, long off)
{
    gui_mac.blink_wait = wait;
    gui_mac.blink_on   = on;
    gui_mac.blink_off  = off;
}

void gui_mch_stop_blink()
{
    // NSLog(@"gui_mch_stop_blink");
    return;
    
    [gui_mac.blink_timer invalidate];

    if (gui_mac.blink_state == BLINK_OFF)
        gui_update_cursor(TRUE, FALSE);

    gui_mac.blink_state = BLINK_NONE;
    gui_mac.blink_timer = nil;
}

void gui_mch_start_blink()
{
    // NSLog(@"gui_mch_start_blink");

    return;

    if (gui_mac.blink_timer != nil)
        [gui_mac.blink_timer invalidate];
    
    if (gui_mac.blink_wait && gui_mac.blink_on && 
        gui_mac.blink_off && gui.in_focus)
    {
        gui_mac.blink_timer = [NSTimer scheduledTimerWithTimeInterval: gui_mac.blink_wait / 1000.0
                                         target: gui_mac.app_delegate
                                       selector: @selector(blinkCursorTimer:)
                                       userInfo: nil
                                        repeats: NO];
        gui_mac.blink_state = BLINK_ON;
        gui_update_cursor(TRUE, FALSE);
    }
}

/* Cursor blinking stuff }}} */

/* GUI tab stuff {{{ */

void gui_mch_set_curtab(int nr)
{
}

void gui_mch_show_tabline(int showit)
{
}

int gui_mch_showing_tabline() { return 0; }

void gui_mch_update_tabline()
{
}

/* GUI tab stuff }}} */

/* Private Functions {{{1 */

int hex_digit(int c)
{
    if (isdigit(c))
        return c - '0';
    
    c = TOLOWER_ASC(c);
    
    if (c >= 'a' && c <= 'f')
        return c - 'a' + 10;
    
    return -1000;
}

NSColor *NSColorFromGuiColor(guicolor_T color, float alpha)
{
    float red, green, blue;

    red   = (float) Red(color) / (float) 0xFF;
    green = (float) Green(color) / (float) 0xFF;
    blue  = (float) Blue(color) / (float) 0xFF;

    return [NSColor colorWithDeviceRed: red
                                 green: green
                                  blue: blue
                                 alpha: alpha];
}

/* Application Related Utilities {{{2 */

@implementation VimAppDelegate

- (void) applicationDidFinishLaunching:(NSNotification *)aNotification
{
    NSLog(@"applicationDidFinishLaunching");
    create_window(gui_mac.content_rect);
}

- (void) initializeApplicationTimer:(NSTimer *)timer
{
    NSLog(@"initializeApplicationTimer fired");
    [NSApp stop: self];
}

- (void) blinkCursorTimer:(NSTimer *)timer
{
    NSTimeInterval on_time, off_time;

    NSLog(@"blinkCursorTimer: %s", gui_mac.blink_state == BLINK_ON ? "BLINK_ON" : 
                                   (gui_mac.blink_state == BLINK_OFF ? "BLINK_OFF" 
                                                                     : "BLINK_NONE"));

    [gui_mac.blink_timer invalidate];
    if (gui_mac.blink_state == BLINK_ON)
    {
        gui_undraw_cursor();
        gui_mac.blink_state = BLINK_OFF;
        
        off_time = gui_mac.blink_off / 1000.0;
        // fprintf(stderr, "off_time = %g\n", off_time);
        
        gui_mac.blink_timer = [NSTimer scheduledTimerWithTimeInterval: off_time
                                         target: gui_mac.app_delegate
                                       selector: @selector(blinkCursorTimer:)
                                       userInfo: nil
                                        repeats: NO];
    }
    else if (gui_mac.blink_state == BLINK_OFF)
    {
        gui_update_cursor(TRUE, FALSE);
        gui_mac.blink_state = BLINK_ON;
        
        on_time = gui_mac.blink_on / 1000.0;
        // fprintf(stderr, "on_time = %g\n", on_time);
        
        gui_mac.blink_timer = [NSTimer scheduledTimerWithTimeInterval: on_time
                                         target: gui_mac.app_delegate
                                       selector: @selector(blinkCursorTimer:)
                                       userInfo: nil
                                        repeats: NO];
    }
}

- (void) menuAction:(id)sender
{
    NSMenuItem *item = (NSMenuItem *) sender;
    vimmenu_T *menu;
    
    /* NSMenuItem does not have a specifically
     * made "user data" to contain vimmenu_T, 
     * so we have to use this trick, cast an
     * int to a pointer, hopefully in all Macs
     * they are in the same length */
    if ((menu = (vimmenu_T *)[item tag]) == NULL)
        return;

    if (menu->cb != NULL)
        gui_menu_cb(menu);
}

- (void) applicationWillTerminate:(NSNotification *)aNotification
{
    // [gui_mac.app_pool release];
}

@end

/* Application Related Utilities 2}}} */

/* Menu Related Utilities {{{2 */

void set_application_menu()
{
    NSMenu *appleMenu, *services;
    NSMenuItem *menuItem;
    NSString *title;
    NSString *appName;
    
    appName = @"Vim";
    appleMenu = [[NSMenu alloc] initWithTitle: @""];

    /* Add menu items */
    title = [@"About " stringByAppendingString: appName];
    [appleMenu addItemWithTitle: title 
                         action: @selector(orderFrontStandardAboutPanel:) 
                  keyEquivalent: @""];

    [appleMenu addItem:[NSMenuItem separatorItem]];

    // Services Menu
    services = [[[NSMenu alloc] init] autorelease];
    [appleMenu addItemWithTitle: @"Services" 
                         action: nil
                  keyEquivalent: @""];
    [appleMenu setSubmenu: services forItem: [appleMenu itemWithTitle: @"Services"]];

    // Hide AppName
    title = [@"Hide " stringByAppendingString:appName];
    [appleMenu addItemWithTitle: title action: @selector(hide:) keyEquivalent: @"h"];

    // Hide Others
    menuItem = (NSMenuItem *)[appleMenu addItemWithTitle: @"Hide Others" 
                                                  action: @selector(hideOtherApplications:) 
                                           keyEquivalent: @"h"];
    [menuItem setKeyEquivalentModifierMask: (NSAlternateKeyMask | NSCommandKeyMask)];

    // Show All
    [appleMenu addItemWithTitle: @"Show All" 
                         action: @selector(unhideAllApplications:) 
                  keyEquivalent: @""];

    [appleMenu addItem: [NSMenuItem separatorItem]];

    // Quit AppName
    title = [@"Quit " stringByAppendingString: appName];
    [appleMenu addItemWithTitle: title 
                         action: @selector(terminate:) 
                  keyEquivalent: @"q"];

    /* Put menu into the menubar */
    menuItem = [[NSMenuItem alloc] initWithTitle: @"" 
                                          action: nil 
                                   keyEquivalent: @""];
    [menuItem setSubmenu: appleMenu];
    [[NSApp mainMenu] addItem: menuItem];

    /* Tell the application object that this is now the application menu */
    [NSApp setAppleMenu: appleMenu];
    [NSApp setServicesMenu: services];

    /* Finally give up our references to the objects */
    [appleMenu release];
    [menuItem release];
}

/* Menu Related Utilities 2}}} */

/* Window related Utilities {{{2 */

int create_window(NSRect rect)
{
    NSWindow    *window;
    unsigned int style;

    style = NSTitledWindowMask | 
    NSMiniaturizableWindowMask | 
          NSClosableWindowMask | 
         NSResizableWindowMask;

    window = [[NSWindow alloc] initWithContentRect: rect 
                                         styleMask: style
                                           backing: NSBackingStoreBuffered 
                                             defer: NO];
    gui_mac.content_window = window;
    
    [window setBackgroundColor: [NSColor clearColor]];
    [window setOpaque: YES];
    [window center];
    [window setViewsNeedDisplay: NO];
    [window setTitle: @"gVIM on Macintosh"];
    [window makeKeyAndOrderFront: nil];
    [window setResizeIncrements: NSMakeSize(gui.char_width, gui.char_height)];

    gui_mac.content_view = [[VimTextView alloc] initWithFrame: rect];
    [gui_mac.content_view setAutoresizingMask: NSViewWidthSizable | 
                                              NSViewHeightSizable];
    [[window contentView] addSubview: gui_mac.content_view];
    [window makeFirstResponder: gui_mac.content_view];

    return OK;
}

/* Window related Utilities 2}}} */

/* View related Utilities 2{{{ */

@implementation VimTextView

- (BOOL) isOpaque
{
    return YES;
}

- (BOOL) hasMarkedText
{
    return NO;
}

- (NSRange) markedRange
{
    return NSMakeRange(NSNotFound, 0);
}

- (NSRange) selectedRange
{
    return NSMakeRange(NSNotFound, 0);
}

- (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
{
    NSLog(@"setMarkedText: %@ (%u, %u)", aString, 
          selRange.location, selRange.length);
}

- (void) unmarkText
{
    NSLog(@"unmarkText");
}

- (NSArray *) validAttributesForMarkedText
{
    return nil;
}

- (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
{
    return nil;
}

#define INLINE_KEY_BUFFER_SIZE                      256
#define add_to_key_buffer(buf, len, k1, k2, k3)     { buf[len++] = k1; buf[len++] = k2; buf[len++] = k3; }

- (void) insertText:(id)aString
{
    char_u *to;
    unichar *text;
    int     i;
    size_t  u16_len, enc_len, result_len = 0;
    char_u  result[INLINE_KEY_BUFFER_SIZE];

    NSLog(@"insertText: %@", aString);

    u16_len = [aString length] * 2;
    text = (unichar *) alloc(u16_len);
    if (! text)
        return;
        
    [aString getCharacters: text];
    to = mac_utf16_to_enc(text, u16_len, &enc_len);

    if (! to)
        return;
    
    /* This is basically add_to_input_buf_csi() */
    for (i = 0; i < enc_len && result_len < (INLINE_KEY_BUFFER_SIZE - 1); ++i)
    {
        result[result_len++] = to[i];
        if (to[i] == CSI)
        {
            result[result_len++] = KS_EXTRA;
            result[result_len++] = (int)KE_CSI;
        }
    }
    vim_free(to);
    
    if (result_len > 0)
    {
        add_to_input_buf(result, result_len);
        stop_app();
    }
}

- (unsigned int) characterIndexForPoint:(NSPoint)thePoint
{
    return NSNotFound;
}

- (NSRect) firstRectForCharacterRange:(NSRange)theRange
{
    return NSMakeRect(0, 0, 0, 0);
}

- (long) conversationIdentifier
{
    return (long) self;
}

- (void) doCommandBySelector:(SEL)aSelector
{
    
}

- (void) drawRect:(NSRect)rect
{
    /* NSLog(@"drawRect: (%f, %f), (%f, %f)", 
          rect.origin.x, rect.origin.y, 
          rect.size.width, rect.size.height);  */
    
    if ([self inLiveResize])
    {
        NSFont *font;
        NSString *size_desc;
        NSDictionary *attrib;
        
        [NSColorFromGuiColor(gui.back_pixel, gui_mac.bg_alpha) set];
        [NSBezierPath fillRect: rect];
        
        font = [NSFont boldSystemFontOfSize: 48.0];
        
        size_desc = [NSString stringWithFormat: @"%d x %d (%d x %d)", 
                        (int) rect.size.width, 
                        (int) rect.size.height, 
                        (int) rect.size.width / gui.char_width, 
                        (int) rect.size.height / gui.char_height];
        attrib = [NSDictionary dictionaryWithObjectsAndKeys: 
                    [NSColor whiteColor], NSForegroundColorAttributeName, 
                    font, NSFontAttributeName, nil];
                        
        NSSize desc_size = [size_desc sizeWithAttributes: attrib];
        
        [size_desc drawAtPoint: NSMakePoint(rect.size.width / 2 - desc_size.width / 2, 
                                            rect.size.height / 2 - desc_size.height / 2)
                withAttributes: attrib];
        return;
    }
    else 
    {
        if (gui_mac.last_draw_string != nil)
        {
            // NSLog(@"last_draw_string: %@", gui_mac.last_draw_string);
            gui_mac.last_draw_string = nil;
        }
#if 1
        [gui_mac.content_image drawInRect: rect
                                 fromRect: FLIPPED_RECT(rect)
                                operation: NSCompositeCopy
                                 fraction: 1.0];
#else
            // NSShowRect("drawRect: content_rect", gui_mac.content_rect);
            [gui_mac.content_image drawInRect: gui_mac.content_rect
                                     fromRect: gui_mac.content_rect
                                    operation: NSCompositeCopy
                                     fraction: 1.0];
#endif
    }
}

- (void) viewDidEndLiveResize
{
    NSSize size;
    int width, height;

    size   = [gui_mac.content_view bounds].size;
    width  = (int) size.width;
    height = (int) size.height;

    NSLog(@"viewDidEndLiveResize: width = %d, height = %d", width, height);

    [gui_mac.content_image release];
    gui_mac.content_image = [[NSImage alloc] initWithSize: NSMakeSize(width, height)];
    [gui_mac.content_image setFlipped: YES];
    gui_mac.content_rect = NSMakeRect(0, 0, width + 10, height + 10);

    gui_resize_shell(width, height);
    gui_set_shellsize(TRUE, FALSE, RESIZE_BOTH);
    
    redraw();
}

- (BOOL) wantsDefaultClipping
{
    return NO;
}

- (BOOL) isFlipped
{
    return NO;
}

- (BOOL) acceptsFirstResponder
{
    return YES;
}

- (void) keyDown:(NSEvent *)event
{
    UniChar         modified_char, original_char;
    unsigned int    mac_modifiers, vim_modifiers;
    char_u          result[INLINE_KEY_BUFFER_SIZE];
    int             len = 0;
    int             vim_key_char;

    /* get key code and modifier flags from event */
    mac_modifiers  = [event modifierFlags];
    
    /* convert NS* style modifier flags to vim style */
    vim_modifiers = modifiers_to_vim(mac_modifiers);
    
    NSLog(@"keyDown: characters = %d\n", [[event characters] length]);

    if ([[event characters] length] == 1)
    { 
        modified_char = [[event characters] characterAtIndex: 0];
        original_char = [[event charactersIgnoringModifiers] characterAtIndex: 0];
        
        /* NOTE: a very dirty hack here. */
        if (original_char == NSPageDownFunctionKey || 
            original_char == NSPageUpFunctionKey)
            gui_mac.disable_redraw = YES;

        // fprintf(stderr, "%04X\n", original_char);    

        /* Intercept CMD-. and CTRL-c */
        if (((vim_modifiers & MOD_MASK_CTRL) && original_char == 'c') ||
            ((vim_modifiers & MOD_MASK_CMD)  && original_char == '.'))
        {
            got_int = TRUE;
            
            stop_app();
            return;
        }
        
        /* if modified character != original character, */
        if (modified_char != original_char)
        {
            vim_modifiers &= ~MOD_MASK_SHIFT;
            vim_modifiers &= ~MOD_MASK_ALT;
        }

        if (vim_modifiers)
            add_to_key_buffer(result, len, CSI, KS_MODIFIER, vim_modifiers);
            

        vim_key_char = function_key_to_vim(original_char, vim_modifiers);
        if (IS_SPECIAL(vim_key_char))
        {
            add_to_key_buffer(result, len, CSI, 
                              K_SECOND(vim_key_char), 
                              K_THIRD(vim_key_char));
                  
            add_to_input_buf(result, len);
            
            stop_app();
            return;
        }
        
        else if (vim_key_char != 0)
        /* must be enter, esc, backspace, tab, add to input buffer directly */
        {
            result[len++] = vim_key_char;
            add_to_input_buf(result, len);
            stop_app();
        }
    }
    
    [self interpretKeyEvents: [NSArray arrayWithObject: event]];
}

@end

/* View related Utilities 2}}} */

/* Drawing related Utilities {{{2 */

void redraw()
{
    [gui_mac.content_view setNeedsDisplay: YES];
}

/* Drawing related Utilities 2}}} */

/* Keyboard Related Utilities 2{{{ */

int function_key_to_vim(UniChar key_char, unsigned int vim_modifiers)
{
    int i;

    for (i = 0; function_key_mapping[i].function_key != 0; i++)
        if (key_char == function_key_mapping[i].function_key)
            return simplify_key(function_key_mapping[i].vim_key,
                                (int *) &vim_modifiers);

    return 0;
}

unsigned int modifiers_to_vim(unsigned int mac_modifiers)
{
    unsigned int vim_modifiers = 0;
        
    if (mac_modifiers & NSShiftKeyMask)
        vim_modifiers |= MOD_MASK_SHIFT;
    if (mac_modifiers & NSControlKeyMask)
        vim_modifiers |= MOD_MASK_CTRL;
    if (mac_modifiers & NSAlternateKeyMask)
        vim_modifiers |= MOD_MASK_ALT;
    if (mac_modifiers & NSCommandKeyMask)
        vim_modifiers |= MOD_MASK_CMD;
        
    return vim_modifiers;
}

/* Keyboard Related Utilities 2}}} */

/* Font Related Utilities 2{{{ */
    
NSFont *get_font(char_u *font_name, int size)
{
    fprintf(stderr, "get_font: %s\n", font_name);
    NSString *mac_font_name = [NSString stringWithUTF8String: (const char *) font_name];
    
    NSLog(@"fontWithName: %@, %d", mac_font_name, size);

    return [NSFont fontWithName: mac_font_name
                           size: size];
}

GuiFont find_font(char_u *font_spec)
{
    int       len = 0, size = VIM_DEFAULT_FONT_SIZE;
    NSFont   *font;
    char_u   *font_style, *p;
    char_u    font_name[VIM_MAX_FONT_NAME_LEN];
    
    fprintf(stderr, "find_font: %s\n", font_spec);

    font_style = vim_strchr(font_spec, ':');
    len = font_style - font_spec;
    
    fprintf(stderr, "len = %d\n", len);
    
    if (len < 0 || len >= VIM_MAX_FONT_NAME_LEN)
        return NOFONT;

    vim_strncpy(font_name, font_spec, len);
    font_name[len] = '\0';

    if (*font_style == ':')
    {
        p = font_style + 1;
        /* Set the values found after ':' */
        while (*p)
        {
            switch (*p++)
            {
            case 'h':
                size = points_to_pixels(p, &p);
                break;
                /*
                 * TODO: Maybe accept width and styles
                 */
            }

            while (*p == ':')
                p++;
        }
    }

    font = get_font(font_name, size); 
    if (font == nil)
    {
        /*
         * Try again, this time replacing underscores in the font name
         * with spaces (:set guifont allows the two to be used
         * interchangeably; the Font Manager doesn't).
         */
        int i, changed = FALSE;

        for (i = font_name[0]; i > 0; --i)
        {
            if (font_name[i] == '_')
            {
                font_name[i] = ' ';
                changed = TRUE;
            }
        }

        if (changed)
            font = get_font(font_name, size);
    }

    if (font == nil)
        return NOFONT;

    [font retain];
    return (long_u) font;
}

int points_to_pixels(char_u *str, char_u **end)
{
    int pixels;
    int points = 0;
    int divisor = 0;

    while (*str)
    {
        if (*str == '.' && divisor == 0)
        {
            /* Start keeping a divisor, for later */
            divisor = 1;
            continue;
        }

        if (! isdigit(*str))
            break;

        points *= 10;
        points += *str - '0';
        divisor *= 10;

        ++str;
    }

    if (divisor == 0)
        divisor = 1;

    pixels = points / divisor;
    *end = str;
    return pixels;
}

/* Font Related Utilities 2}}} */

/* Private Functions 1}}} */
